<?php
/**
 *+------------------
 * madong
 *+------------------
 * Copyright (c) https://gitee.com/motion-code  All rights reserved.
 *+------------------
 * Author: Mr. April (405784684@qq.com)
 *+------------------
 * Official Website: http://www.madong.tech
 */

namespace madong\think\wf\dao;

use madong\helper\DateTime;
use madong\think\wf\basic\BaseDao;
use madong\ingenious\libs\utils\ArrayHelper;
use madong\think\wf\basic\BaseModel;
use madong\think\wf\common\Util;
use madong\think\wf\model\ProcessTaskActor;
use madong\think\wf\model\ProcessTask;
use madong\think\wf\model\ProcessInstance;
use madong\think\wf\model\ProcessDefine;
use think\facade\Db;
use madong\ingenious\enums\ProcessConstEnum;

class ProcessTaskActorDao extends BaseDao
{

    protected function setModel(): string
    {
        return ProcessTaskActor::class;
    }

    /**
     * 获取任务列表-think-orm 不支持whereHas 所有只能通过连表方式实现 可以使用模型自定义实现
     *
     * @param $where
     * @param $field
     * @param $page
     * @param $limit
     * @param $order
     * @param $with
     *
     * @return array
     */
    public function getList($where = [], $field = ['*'], $page = 0, $limit = 0, $order = '', $with = []): array
    {
        // 表前缀
        $prefixes = [
            'pa' => 'pa',
            'pt' => 'pt',
            'pd' => 'pd',
            'pi' => 'pi',
        ];

        // 过滤条件
        $where = $this->buildWhere($where, $prefixes);

        // 获取表名
        $tableNames = $this->getTableNames();

        // 定义字段
        $field = $this->defineFields($prefixes);

        // 获取数据库链接
        $connection = Util::getConnectionConfig();

        // 执行查询
        $query = $this->buildQuery($tableNames, $field, $where, $prefixes, $connection);

        // 查询分页
        $total = $query->count();

        // 选择字段并获取数据
        $data = $query->page($page, $limit)->select()->toArray();

        // 处理数据，构建嵌套结构
        $result = $this->formatResults($data);

        return [
            $result,
            $total,
        ];
    }

    private function buildWhere(array $where, array $prefixes): array
    {
        $conditions = [];
        $conditions = array_merge(
            $conditions,
            ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
                ['actor_id', '', '', $prefixes['pa'] . '.actor_id'],
            ])),
            ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
                ['task_name', '', '', $prefixes['pt'] . '.name'],
                ['task_state', '', '', $prefixes['pt'] . '.task_state'],
                ['perform_type', '', '', $prefixes['pt'] . '.perform_type'],
                ['task_display_name', '', '', $prefixes['pt'] . '.display_name'],
                ['operator', '', '', $prefixes['pt'] . '.operator'],
            ])),
            ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
                ['business_no', '', '', $prefixes['pi'] . '.business_no'],
                ['instance_name', '', '', $prefixes['pi'] . '.name'],
                ['instance_display_name', '', '', $prefixes['pi'] . '.display_name'],
            ])),
            ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
                ['process_name', '', '', $prefixes['pd'] . '.name'],
                ['process_display_name', '', '', $prefixes['pd'] . '.display_name'],
            ]))
        );

        return $conditions;
    }

    private function getTableNames(): array
    {
        return [
            'actor'    => ProcessTaskActor::getTableName(),
            'task'     => ProcessTask::getTableName(),
            'define'   => ProcessDefine::getTableName(),
            'instance' => ProcessInstance::getTableName(),
        ];
    }

    private function defineFields(array $prefixes): string
    {
        $actorField = Util::prefixFields([
            'id', 'process_task_id', 'actor_id', 'create_time', 'create_by', 'update_time', 'update_by',
        ], $prefixes['pa'] . '.');

        $taskField = Util::prefixFields([
            'id', 'process_instance_id', 'task_name', 'display_name', 'task_type', 'perform_type',
            'task_state', 'operator', 'finish_time', 'expire_time', 'form_key', 'task_parent_id',
            'variable', 'create_time', 'create_by', 'update_time', 'update_by',
        ], $prefixes['pt'] . '.');

        $instanceField = Util::prefixFields([
            'id', 'parent_id', 'process_define_id', 'state', 'parent_node_name', 'business_no',
            'operator', 'variable', 'expire_time', 'create_time', 'create_user', 'update_time', 'update_user',
        ], $prefixes['pi'] . '.');

        $defineField = Util::prefixFields([
            'id', 'type_id', 'icon', 'name', 'display_name', 'description', 'enabled',
            'is_active', 'content', 'version', 'create_time', 'create_user', 'update_time', 'update_user', 'delete_time',
        ], $prefixes['pd'] . '.');

        return Util::mergeFieldStrings([$actorField, $taskField, $instanceField, $defineField]);
    }

    private function buildQuery(array $tableNames, string $field, array $where, array $prefixes, $connection = null)
    {
        return Db::connect($connection)
            ->table($tableNames['actor'])
            ->alias(explode('.', $prefixes['pa'])[0]) // 使用前缀作为别名
            ->field($field)
            ->join([$tableNames['task'] => $prefixes['pt']], 'pa.process_task_id = pt.id')
            ->join([$tableNames['instance'] => $prefixes['pi']], 'pt.process_instance_id = pi.id')
            ->join([$tableNames['define'] => $prefixes['pd']], 'pi.process_define_id = pd.id')
            ->where($where)
            ->order('pa.create_time', 'desc');
    }

    private function formatResults(array $data): array
    {
        $result = [];
        foreach ($data as $item) {
            $result[] = [
                'id'              => $item['pa_id'],
                'process_task_id' => $item['pa_process_task_id'],
                'actor_id'        => $item['pa_actor_id'],
                'create_time'     => $item['pa_create_time'],
                'create_by'       => $item['pa_create_by'],
                'update_time'     => $item['pa_update_time'],
                'update_by'       => $item['pa_update_by'],
                'create_date'     => DateTime::timestampToString($item['pa_create_time']),
                'update_date'     => DateTime::timestampToString($item['pa_update_time']),
                'task'            => $this->formatTask($item),
                'instance'        => $this->formatInstance($item),
            ];
        }
        return $result;
    }

    private function formatTask(array $item): array
    {
        return [
            'id'                  => $item['pt_id'],
            'process_instance_id' => $item['pt_process_instance_id'],
            'task_name'           => $item['pt_task_name'],
            'display_name'        => $item['pt_display_name'],
            'task_type'           => $item['pt_task_type'],
            'perform_type'        => $item['pt_perform_type'],
            'task_state'          => $item['pt_task_state'],
            'operator'            => $item['pt_operator'],
            'finish_time'         => $item['pt_finish_time'],
            'expire_time'         => $item['pt_expire_time'],
            'variable'            => Util::convertToArrayOrObject($item['pt_variable'], [], true),
            'create_time'         => $item['pt_create_time'],
            'create_by'           => $item['pt_create_by'],
            'update_time'         => $item['pt_update_time'],
            'update_by'           => $item['pt_update_by'],
            'create_date'         => DateTime::timestampToString($item['pt_create_time']),
            'update_date'         => DateTime::timestampToString($item['pt_update_time']),
            'expire_date'         => DateTime::timestampToString($item['pt_expire_time']),
        ];
    }

    private function formatInstance(array $item): array
    {
        return [
            'id'                => $item['pi_id'],
            'parent_id'         => $item['pi_parent_id'],
            'process_define_id' => $item['pi_process_define_id'],
            'state'             => $item['pi_state'],
            'business_no'       => $item['pi_business_no'],
            'operator'          => $item['pi_operator'],
            'variable'          => Util::convertToArrayOrObject($item['pi_variable'], [], true),
            'form_data'         => Util::getFormData($item['pi_variable']),
            'create_time'       => $item['pi_create_time'],
            'create_user'       => $item['pi_create_user'],
            'update_time'       => $item['pi_update_time'],
            'update_user'       => $item['pi_update_user'],
            'create_date'       => DateTime::timestampToString($item['pi_create_time']),
            'update_date'       => DateTime::timestampToString($item['pi_update_time']),
            'define'            => $this->formatDefine($item),
        ];
    }

    private function formatDefine(array $item): array
    {
        return [
            'id'           => $item['pd_id'],
            'type_id'      => $item['pd_type_id'],
            'icon'         => $item['pd_icon'],
            'name'         => $item['pd_name'],
            'display_name' => $item['pd_display_name'],
            'description'  => $item['pd_description'],
            'enabled'      => $item['pd_enabled'],
            'is_active'    => $item['pd_is_active'],
            'content'      => Util::convertToArrayOrObject($item['pd_content'], [], true),
            'version'      => $item['pd_version'],
            'create_time'  => $item['pd_create_time'],
            'create_user'  => $item['pd_create_user'],
            'update_time'  => $item['pd_update_time'],
            'update_user'  => $item['pd_update_user'],
            'create_date'  => DateTime::timestampToString($item['pd_create_time']),
            'update_date'  => DateTime::timestampToString($item['pd_update_time']),
        ];
    }
}
